package gui;

import data.ObjectCloner;
import java.awt.Window;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import javax.swing.JFileChooser;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Style;
import javax.swing.text.StyleContext;
import tablature.BarLine;
import tablature.Measure;


/**
 *
 * @author eric
 */
public class ExportWindow extends javax.swing.JPanel implements ChangeListener
{

    private ArrayList <Measure> originalMeasures;


    private DefaultStyledDocument document;

    // Plain and Bold styles;
    private Style PLAIN;

    private int caretPosition;


    /** Creates new form ExportWindow */
    public ExportWindow(ArrayList <Measure> measures)
    {
        this.originalMeasures = measures;

        StyleContext context = new StyleContext();
        document = new DefaultStyledDocument(context);
        PLAIN = document.addStyle("PLAIN", null);

        for (Measure m : measures)
        {
           m.setTimingBar(new BarLine("", m.getTimeSignature()));
        }

        

        initComponents();

        maxMeasureSpinner.addChangeListener(this);
        adjustMeasuresSpinner.addChangeListener(this);

        songTextField.getDocument().addDocumentListener(new TextFieldListener());
        bandTextField.getDocument().addDocumentListener(new TextFieldListener());
        albumTextField.getDocument().addDocumentListener(new TextFieldListener());
        drummerTextField.getDocument().addDocumentListener(new TextFieldListener());
        tabberTextField.getDocument().addDocumentListener(new TextFieldListener());


        updateWindow();

        caretPosition = 0;



        
    }

    public String getSongInformation()
    {
        String songInfo = "";
        songInfo = "Song: " + songTextField.getText() +
                "\nBand: " + bandTextField.getText() +
                "\nAlbum: " + albumTextField.getText() +
                "\nDrummer: " + drummerTextField.getText() +
                "\nTabber: " + tabberTextField.getText() + "\n\n";

        return songInfo;

    }


    public void updateWindow()
    {
        caretPosition = previewTextPane.getCaretPosition();

        try
        {
            document.remove(0, document.getLength());
            document.insertString(0, getSongInformation(), PLAIN);
            document.insertString(document.getLength(), getMeasures(), PLAIN);
        } catch (BadLocationException ex)
        {
            ex.printStackTrace();
        }

        previewTextPane.setCaretPosition(caretPosition);
    }


    public JPanel getMainComponent()
    {
        return this;
    }

    /**
     * Saves the tablature to a file.
     */
    private void saveTablature()
    {
        JFileChooser fc = new JFileChooser();
        int returnVal = fc.showSaveDialog(this);
        if (returnVal == JFileChooser.APPROVE_OPTION)
        {
            FileWriter fstream = null;
            try
            {
                File file = fc.getSelectedFile();
                fstream = new FileWriter(file);
                BufferedWriter out = new BufferedWriter(fstream);
                out.write(document.getText(0, document.getLength()));
                //Close the output stream
                out.close();
                closeWindow();
            } catch (Exception ex)
            {
                System.out.println("Could not save the tablature.");
            }
            finally
            {
                try
                {
                    fstream.close();
                }
                catch (IOException ex)
                {
                    System.out.println("Could not close the file.");
                }
            }
        }
        else
        {


        }
    }

    private String getMeasures()
    {
        String output = "";

        ArrayList <Measure> measures = new ArrayList();
        try
        {
            measures = (ArrayList<Measure>) ObjectCloner.deepCopy(originalMeasures);
        } catch (Exception ex)
        {
            ex.printStackTrace();
        }

        

        int adjustMeasuresLength = (Integer) adjustMeasuresSpinner.getValue();

        if (adjustMeasuresLength < 0)
        {
            for (int adjustment = 0; adjustment > adjustMeasuresLength; adjustment--)
            {
                for (int measureNum = 0; measureNum < measures.size(); measureNum++)
                {
                    Measure curMeasure = measures.get(measureNum);

                    for (int barLineNum = 0; barLineNum < curMeasure.getNumberOfBars(); barLineNum++)
                    {
                        BarLine curBarLine = curMeasure.getBarLine(barLineNum);
                        for (int note = 0; note < curBarLine.getLength() - 1; note++)
                        {
                            curBarLine.setNote(note, curBarLine.getNote(note + 1));
                        }

                        char nextNote = BarLine.REST;
                        if (measureNum < measures.size() - 1)
                        {
                            Measure nextMeasure = measures.get(measureNum + 1);
                            nextNote = nextMeasure.getBarLine(barLineNum).getNote(0);
                        }
                        curBarLine.setNote(curBarLine.getLength() - 1, nextNote);

                        curMeasure.setBarLine(barLineNum, curBarLine);
                    }
                    curMeasure.setTimingBar(new BarLine("", curMeasure.getTimeSignature()));
                    measures.set(measureNum, curMeasure);
                }
            }
        }
        else if (adjustMeasuresLength > 0)
        {
            for (int adjustment = 0; adjustment < adjustMeasuresLength; adjustment++)
            {
                for (int measureNum = measures.size() - 1; measureNum >= 0; measureNum--)
                {
                    Measure curMeasure = measures.get(measureNum);

                    for (int barLineNum = 0; barLineNum < curMeasure.getNumberOfBars(); barLineNum++)
                    {
                        BarLine curBarLine = curMeasure.getBarLine(barLineNum);
                        
                        for (int note = curBarLine.getLength() -1; note > 0; note--)
                        {
                            curBarLine.setNote(note, curBarLine.getNote(note - 1));
                        }

                        char nextNote = BarLine.REST;
                        if (measureNum > 0)
                        {
                            Measure prevMeasure = measures.get(measureNum - 1);
                            BarLine barLine = prevMeasure.getBarLine(barLineNum);
                            nextNote = barLine.getNote(barLine.getLength() - 1);
                        }
                        curBarLine.setNote(0, nextNote);

                        

                        curMeasure.setBarLine(barLineNum, curBarLine);
                    }
                    curMeasure.setTimingBar(new BarLine("", curMeasure.getTimeSignature()));
                    measures.set(measureNum, curMeasure);
                }
            }

        }


        for (int measureNum = 0; measureNum < measures.size(); measureNum+= (Integer)maxMeasureSpinner.getValue())
        {
            Measure curMeasure = measures.get(measureNum);
            int barLinesTotal = curMeasure.getNumberOfBars();


            for (int barLineNum = 0; barLineNum < barLinesTotal; barLineNum++)
            {
                for(int measureInLine = 0; measureInLine < (Integer)maxMeasureSpinner.getValue(); measureInLine++)
                {
                    int curMeasurePos = measureNum + measureInLine;
                    if (curMeasurePos < measures.size())
                    {
                        curMeasure = measures.get(curMeasurePos);
                        if (measureInLine == 0)
                        {
                            String componentName = curMeasure.getComponentName(barLineNum);
                            output += curMeasure.getPaddingSpaces(componentName);
                            output += componentName;
                        }

                        output += Measure.BAR_LINE;
                        output += curMeasure.getBarLine(barLineNum).toString();

                        if (measureInLine == (Integer) maxMeasureSpinner.getValue() - 1 || curMeasurePos == measures.size() - 1)
                        {
                            output += Measure.BAR_LINE;
                            output += "\n";
                        }
                    }
                }
            }
            
        }

        return output;
    }

    /**
     * Closes the window.
     */
    private void closeWindow()
    {
        Window window = SwingUtilities.getWindowAncestor(this);
        window.dispose();
    }

    public void stateChanged(ChangeEvent e)
    {
        if (e.getSource() == maxMeasureSpinner)
        {
            updateWindow();
        }
        else if (e.getSource() == adjustMeasuresSpinner)
        {
            updateWindow();
        }
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        exportSplitPane = new javax.swing.JSplitPane();
        previewScrollPane = new javax.swing.JScrollPane();
        previewTextPane = new javax.swing.JTextPane();
        leftSidePanel = new javax.swing.JPanel();
        buttonPanel = new javax.swing.JPanel();
        exportTablature = new javax.swing.JButton();
        optionsPanel = new javax.swing.JPanel();
        maxMeasureSpinner = new javax.swing.JSpinner();
        maxMeasuresLabel = new javax.swing.JLabel();
        songInformationPanel = new javax.swing.JPanel();
        songLabel = new javax.swing.JLabel();
        songTextField = new javax.swing.JTextField();
        bandTextField = new javax.swing.JTextField();
        bandLabel = new javax.swing.JLabel();
        albumLabel = new javax.swing.JLabel();
        albumTextField = new javax.swing.JTextField();
        tabberLabel = new javax.swing.JLabel();
        tabberTextField = new javax.swing.JTextField();
        drummerLabel = new javax.swing.JLabel();
        drummerTextField = new javax.swing.JTextField();
        adjustMeasuresSpinner = new javax.swing.JSpinner();
        adjustMeasuresLabel = new javax.swing.JLabel();

        previewTextPane.setDocument(document);
        previewTextPane.setFont(new java.awt.Font("Bitstream Vera Sans Mono", 0, 14)); // NOI18N
        previewScrollPane.setViewportView(previewTextPane);

        exportSplitPane.setRightComponent(previewScrollPane);

        exportTablature.setFont(new java.awt.Font("Bitstream Vera Sans", 1, 14)); // NOI18N
        exportTablature.setIcon(new javax.swing.ImageIcon(getClass().getResource("/img/export.png"))); // NOI18N
        exportTablature.setText("Save Tablature");
        exportTablature.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                exportTablatureActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout buttonPanelLayout = new javax.swing.GroupLayout(buttonPanel);
        buttonPanel.setLayout(buttonPanelLayout);
        buttonPanelLayout.setHorizontalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(buttonPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addComponent(exportTablature)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        buttonPanelLayout.setVerticalGroup(
            buttonPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(buttonPanelLayout.createSequentialGroup()
                .addComponent(exportTablature)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        maxMeasureSpinner.setModel(new javax.swing.SpinnerNumberModel(Integer.valueOf(1), Integer.valueOf(1), null, Integer.valueOf(1)));

        maxMeasuresLabel.setText("Max measures per line");

        songInformationPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Song Information"));

        songLabel.setText("Song");

        bandLabel.setText("Band");

        albumLabel.setText("Album");

        tabberLabel.setText("Tabber");

        drummerLabel.setText("Drummer");

        javax.swing.GroupLayout songInformationPanelLayout = new javax.swing.GroupLayout(songInformationPanel);
        songInformationPanel.setLayout(songInformationPanelLayout);
        songInformationPanelLayout.setHorizontalGroup(
            songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(songInformationPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(songInformationPanelLayout.createSequentialGroup()
                        .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(songLabel)
                            .addComponent(bandLabel)
                            .addComponent(albumLabel)
                            .addComponent(drummerLabel))
                        .addGap(21, 21, 21)
                        .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(songTextField, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                            .addComponent(tabberTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                            .addComponent(albumTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                            .addComponent(bandTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)
                            .addComponent(drummerTextField, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 100, Short.MAX_VALUE)))
                    .addComponent(tabberLabel))
                .addContainerGap())
        );
        songInformationPanelLayout.setVerticalGroup(
            songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(songInformationPanelLayout.createSequentialGroup()
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(songTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(songLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(bandLabel)
                    .addComponent(bandTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(albumLabel)
                    .addComponent(albumTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(drummerLabel)
                    .addComponent(drummerTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(songInformationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(tabberLabel)
                    .addComponent(tabberTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addContainerGap(74, Short.MAX_VALUE))
        );

        adjustMeasuresSpinner.setModel(new javax.swing.SpinnerNumberModel());

        adjustMeasuresLabel.setText("Adjust Notes");

        javax.swing.GroupLayout optionsPanelLayout = new javax.swing.GroupLayout(optionsPanel);
        optionsPanel.setLayout(optionsPanelLayout);
        optionsPanelLayout.setHorizontalGroup(
            optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(optionsPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(maxMeasureSpinner)
                    .addComponent(adjustMeasuresSpinner, javax.swing.GroupLayout.DEFAULT_SIZE, 41, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(adjustMeasuresLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(maxMeasuresLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addGap(28, 28, 28))
            .addGroup(optionsPanelLayout.createSequentialGroup()
                .addComponent(songInformationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGap(19, 19, 19))
        );
        optionsPanelLayout.setVerticalGroup(
            optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(optionsPanelLayout.createSequentialGroup()
                .addGap(34, 34, 34)
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(maxMeasureSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(maxMeasuresLabel))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addGroup(optionsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(adjustMeasuresSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(adjustMeasuresLabel))
                .addGap(18, 18, 18)
                .addComponent(songInformationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap())
        );

        javax.swing.GroupLayout leftSidePanelLayout = new javax.swing.GroupLayout(leftSidePanel);
        leftSidePanel.setLayout(leftSidePanelLayout);
        leftSidePanelLayout.setHorizontalGroup(
            leftSidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(leftSidePanelLayout.createSequentialGroup()
                .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
            .addComponent(optionsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
        );
        leftSidePanelLayout.setVerticalGroup(
            leftSidePanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, leftSidePanelLayout.createSequentialGroup()
                .addComponent(optionsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
        );

        exportSplitPane.setLeftComponent(leftSidePanel);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(exportSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 777, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(exportSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 388, Short.MAX_VALUE))
        );
    }// </editor-fold>//GEN-END:initComponents

    private void exportTablatureActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_exportTablatureActionPerformed
    {//GEN-HEADEREND:event_exportTablatureActionPerformed
        saveTablature();
    }//GEN-LAST:event_exportTablatureActionPerformed


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JLabel adjustMeasuresLabel;
    private javax.swing.JSpinner adjustMeasuresSpinner;
    private javax.swing.JLabel albumLabel;
    private javax.swing.JTextField albumTextField;
    private javax.swing.JLabel bandLabel;
    private javax.swing.JTextField bandTextField;
    private javax.swing.JPanel buttonPanel;
    private javax.swing.JLabel drummerLabel;
    private javax.swing.JTextField drummerTextField;
    private javax.swing.JSplitPane exportSplitPane;
    private javax.swing.JButton exportTablature;
    private javax.swing.JPanel leftSidePanel;
    private javax.swing.JSpinner maxMeasureSpinner;
    private javax.swing.JLabel maxMeasuresLabel;
    private javax.swing.JPanel optionsPanel;
    private javax.swing.JScrollPane previewScrollPane;
    private javax.swing.JTextPane previewTextPane;
    private javax.swing.JPanel songInformationPanel;
    private javax.swing.JLabel songLabel;
    private javax.swing.JTextField songTextField;
    private javax.swing.JLabel tabberLabel;
    private javax.swing.JTextField tabberTextField;
    // End of variables declaration//GEN-END:variables


    /**
     * Sub-Class: TextFieldListener
     * Description: This sub class of ModifyComponentWindow.java simply
     * listens for any change from any of the text boxes. If it hears
     * one, it checks all the requirement labels to see if they should
     * change from red to green or vice-versa.
     * Then it calls updateAddButton() to see if we can now
     * legitamitely can add the component now.
     * @author: et
     */
    class TextFieldListener implements DocumentListener
    {
        /**
         * Something was added to a text field.
         * @param e - document event
         */
        public void insertUpdate(DocumentEvent e)
        {
            updateWindow();
        }

        /**
         * Something was removed from a text field.
         * @param e - document event.
         */
        public void removeUpdate(DocumentEvent e)
        {
           updateWindow();
        }

        /**
         * Something was changed in a text field.
         * @param e - document event.
         */
        public void changedUpdate(DocumentEvent e)
        {
            updateWindow();
        }
    }


}
